﻿using System;
using System.Xml;
using Notoric.Model.Internal;
using Notoric.Model.Packaging;
using System.ComponentModel;

namespace Notoric.Model.Collections
{
    /// <summary>
    /// Holds a mapping of a virtual path to a part uri.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The class data are backed up by an XML element.   
    /// </para>
    /// <para>
    /// This class overrides the <see cref="Object.ToString"/> method so that it returns
    /// a readable string representation of this object.
    /// </para>
    /// </remarks>
    public sealed class PathElement : INotifyPropertyChanged
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="PathElement"/> instance.
        /// </summary>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="PathCollection"/>
        /// </para>
        /// <para>
        /// The package part that owns this element.
        /// </para>
        /// </param>
        /// <param name="virtualPath">
        /// <para>
        /// Type: <see cref="System.String"/>
        /// </para>
        /// <para>
        /// The virtual path that is being mapped.
        /// </para>
        /// </param>
        /// <param name="partUri">
        /// <para>
        /// Type: <see cref="Uri"/>
        /// </para>
        /// <para>
        /// The part URI to which the path is being mapped.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="owner"/>, <paramref name="virtualPath"/> or <paramref name="partUri"/> is <see langword="null"/>.
        /// </exception>
        internal PathElement(PathCollection owner, string virtualPath, Uri partUri)
        {
            if (owner == null)
                throw new ArgumentNullException("owner");
            if (virtualPath == null)
                throw new ArgumentNullException("virtualPath");
            if (partUri == null)
                throw new ArgumentNullException("partUri");

            var document = owner.CollectionElement.OwnerDocument;

            element = document.CreateElement(
                PackageVocabulary.PackageNamespacePrefix,
                PackageVocabulary.PathElementName,
                PackageVocabulary.PackageNamespaceUri
                );

            pathAttribute = document.CreateAttribute(PackageVocabulary.PathAttribute);
            uriAttribute = document.CreateAttribute(PackageVocabulary.UriAttribute);

            element.Attributes.Append(pathAttribute);
            element.Attributes.Append(uriAttribute);

            pathAttribute.Value = virtualPath;
            uriAttribute.Value = partUri.ToString(); ;
        }

        /// <summary>
        /// Creates a new <see cref="PathElement"/> instance and loads its data from a serialized XML representation.
        /// </summary>
        /// <param name="element">
        /// <para>
        /// Type: <see cref="XmlElement"/>
        /// </para>
        /// <para>
        /// The XML element that holds the data of this object.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="element"/> is <see langword="null"/>.
        /// </exception>
        internal PathElement(XmlElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            this.element = element;
            pathAttribute = element.Attributes[PackageVocabulary.PathAttribute];
            uriAttribute = element.Attributes[PackageVocabulary.UriAttribute];
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Occurs when the value of some property has changed.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Object Members

        /// <inheritdoc/>
        public override string ToString()
        {
            return String.Format("Virtual Path {0} mapped to part at {1}", VirtualPath, URI);
        }

        #endregion

        #region Internal Properties

        /// <summary>
        /// Gets the XML element that holds the data of this object.
        /// </summary>
        internal XmlElement Element
        {
            get { return element; }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the URI of the package part that is being mapped by this path element.
        /// </summary>
        public Uri URI
        {
            get { return new Uri(uriAttribute.Value, UriKind.Relative); }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                uriAttribute.Value = value.ToString();
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("URI"));
            }
        }

        /// <summary>
        /// Gets or sets the virtual path of the package part that is being mapped by this element.
        /// </summary>
        public string VirtualPath
        {
            get { return pathAttribute.Value; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");

                pathAttribute.Value = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("VirtualPath"));
            }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds a reference to the XML element that holds the data of this object.
        /// </summary>
        private XmlElement element;
        /// <summary>
        /// Holds a reference to the XML attribute that holds the data of the <see cref="VirtualPath"/> property.
        /// </summary>
        private XmlAttribute pathAttribute;
        /// <summary>
        /// Holds a reference to the XML attribute that holds the data of the <see cref="URI"/> property.
        /// </summary>
        private XmlAttribute uriAttribute;

        #endregion
    }
}
